//
// Created by QU on 24-8-23.
//
#pragma once

#include <boost/asio.hpp>

#include <iostream>

namespace asio = boost::asio;

using tcp = asio::ip::tcp;
using udp = asio::ip::udp;

class Session : public std::enable_shared_from_this<Session>
{
public:
    /**
         shared_ptr 的基本概念
        std::shared_ptr 是 C++ 标准库中的智能指针，用于自动管理动态分配的对象的生命周期。它通过引用计数的方式管理资源，确保在不再需要使用对象时自动释放内存。

        特点：
        引用计数：shared_ptr 内部维护一个引用计数器，记录有多少个 shared_ptr 实例指向同一个对象。每当有一个 shared_ptr 指向该对象时，计数加一；每当一个 shared_ptr 离开作用域或被重置时，计数减一。当计数变为零时，shared_ptr 自动释放内存。
        自动释放内存：当最后一个指向对象的 shared_ptr 被销毁时，shared_ptr 会自动调用 delete 来释放资源，避免内存泄漏。
        线程安全：多个线程可以安全地共享和使用 shared_ptr。
        为什么需要 shared_ptr
        在异步操作中（如 Boost.Asio 的异步 I/O 操作），我们通常无法预测回调函数何时会被调用。因此，如果一个对象在异步操作完成之前被销毁，程序可能会崩溃或产生未定义行为。shared_ptr 能确保对象在异步操作期间保持有效，防止过早销毁。

        enable_shared_from_this 的作用
        std::enable_shared_from_this 是一个辅助基类，允许类的成员函数安全地创建一个指向自身的 shared_ptr。这个机制非常有用，尤其是在类的方法中需要返回 shared_ptr 或在异步回调中确保对象的生命周期时。

        为什么需要 enable_shared_from_this？
        如果你直接在类的成员函数中通过 std::make_shared 创建一个新的 shared_ptr，这个新的 shared_ptr 与外部的 shared_ptr 会是独立的，会导致两个不同的引用计数器管理同一个对象。这是很危险的，因为对象可能会在引用计数器变为零时被提前销毁。

        enable_shared_from_this 解决了这个问题。它允许对象在类的内部通过 shared_from_this() 方法获取指向自身的 shared_ptr，这个 shared_ptr 会和外部的 shared_ptr 共享同一个引用计数器，从而避免上述问题。

        为什么在实例中需要它们
        在你提供的代码中，Session 对象在处理异步操作时，需要确保对象在整个异步操作的生命周期内不会被销毁。如果你只是用裸指针或不正确地管理 shared_ptr，可能会导致对象在异步操作完成之前被销毁，导致程序崩溃或产生不正确的行为。

        通过使用 shared_ptr 来管理 Session 的生命周期，再结合 enable_shared_from_this，我们可以确保：

        Session 对象的生命周期在异步操作完成之前是安全的。
        可以在类的内部方法中安全地获取和共享同一个 shared_ptr，避免因引用计数器混乱而导致的提前销毁。
        这就是为什么在异步操作的上下文中，shared_ptr 和 enable_shared_from_this 是如此重要的原因。它们确保对象生命周期被正确管理，避免未定义行为和潜在的内存管理问题。
     */

    Session(asio::io_context& io_context) : socket_(io_context)
    {
        std::cout << "Session created" << std::endl;
    }

    ~Session()
    {
        std::cout << "Session destroyed" << std::endl;
    }

    tcp::socket& socket() { return socket_; }

    void start()
    {
        std::cout << "Session started" << std::endl;
        // memset()............
        // std::fill(std::begin(data_), std::end(data_), 0);            // c++11
        std::ranges::fill(data_, 0); // c++20
        auto self(shared_from_this()); // 获取一个自身的 shared_ptr
        socket_.async_receive(asio::buffer(data_, MAX_LENGTH),
                              [self, this](std::error_code error_code, std::size_t bytes_transferred)
                              {
                                  handler_read(error_code, bytes_transferred);
                              }
        );
    }

private:
    void handler_read(const boost::system::error_code& error_code, size_t bytes_transferred)
    {
        std::cout << "Handling read" << std::endl;
        if (error_code)
        {
            if (error_code == asio::error::eof)
            {
                std::cout << "Received EOF." << std::endl;
                return;
            }
            std::cerr << "Handle an error: " << error_code.value() << ". Message: " << error_code.message() <<
                std::endl;
            // throw std::runtime_error("Received error.");
            return;
        }

        std::cout << "Received bytes: " << bytes_transferred << std::endl;
        std::cout << "Content: " << data_ << std::endl;

        // write ECHO
        auto self(shared_from_this()); // 获取一个自身的 shared_ptr

        // llfc 在(5) 52'46" 说一般开发的时候要将读不停地挂在队列中.
        // 他当时使用的是 new/delete, 在handle_write写完了, 应该delete Session.
        // 但是读当中还有关于这个Session的监听队列事件, 导致崩溃.

        // 但是我一开始就用的是make_shared, Session也加了enable_shared_form_this, 所以没有问题出现... 23333.
        socket_.async_receive(asio::buffer(data_, bytes_transferred),
                              [self, this](boost::system::error_code error_code, std::size_t bytes_transferred)
                              {
                                  this->handler_read(error_code, bytes_transferred);
                              });

        socket_.async_send(boost::asio::buffer(data_, bytes_transferred),
                           [self, this](const boost::system::error_code& ec, size_t bytes_transferred)
                           {
                               this->handler_write(ec, bytes_transferred);
                           }
        );
    }

    void handler_write(const boost::system::error_code& error_code, size_t bytes_transferred)
    {
        std::cout << "Handling write" << std::endl;
        if (error_code)
        {
            std::cerr << "Handle an error: " << error_code.value() << ". Message: " << error_code.message()
                << std::endl;
            // throw std::runtime_error("Send failed.");
            return;
        }

        auto self(shared_from_this()); // 获取一个自身的 shared_ptr
        socket_.async_receive(boost::asio::buffer(data_, bytes_transferred),
                              [self, this](const boost::system::error_code& error_code, size_t bytes_transferred)
                              {
                                  handler_read(error_code, bytes_transferred);
                              }
        );
    }

    tcp::socket socket_;
    static constexpr size_t MAX_LENGTH = 1024;
    char data_[MAX_LENGTH] { 0 };
};

class Server
{
public:
    Server(asio::io_context& io_context, unsigned short port) :
        io_context_(io_context), acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
    {
        std::cout << "Server starting on port: " << port << std::endl;
        start_accept();
    }

private:
    void start_accept()
    {
        std::cout << "Server accepting connections" << std::endl;
        auto new_session_p = std::make_shared<Session>(io_context_);

        acceptor_.async_accept(new_session_p->socket(),
                               // 这里的捕获列表中不能使用 &, 会导致shared_ptr在这个异步调用中被析构.
                               [=, this](const boost::system::error_code& code)
                               {
                                   handle_accept(new_session_p, code);
                               }
                               // std::bind(&handle_accept, this, )
        );
    }

    void handle_accept(std::shared_ptr<Session> session_p, const boost::system::error_code& error_code)
    {
        std::cout << "Handling accept" << std::endl;
        if (error_code)
        {
            std::cerr << "Handle error: " << error_code.value() << ". Message: " << error_code.message()
                << std::endl;
            return;
        }

        session_p->start();
        start_accept();
    }

    // io_context 不允许拷贝.   我们这里整一个引用.
    asio::io_context& io_context_;
    asio::ip::tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
    asio::io_context io_context;

    // Server server(io_context, 10120);
    // io_context.run();
    try
    {
        Server server(io_context, 10120);
        io_context.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
}
